﻿using System.CommandLine;

namespace zijian666.CommandLine;
/// <summary>
/// 构建命令行选项的接口。
/// </summary>
internal interface IOptionBuilder
{
    /// <summary>
    /// 根据 <see cref="OptionAttribute"/> 构建 <see cref="Option"/> 实例。
    /// </summary>
    /// <param name="attribute">选项的属性信息。</param>
    /// <returns>构建的 <see cref="Option"/> 实例。</returns>
    Option Build(OptionAttribute attribute);

    /// <summary>
    /// 从解析结果中获取选项的值。
    /// </summary>
    /// <param name="option">命令行选项。</param>
    /// <param name="result">解析结果。</param>
    /// <returns>选项的值。</returns>
    object GetValue(Option option, ParseResult result);

    /// <summary>
    /// 创建指定类型的 <see cref="IOptionBuilder"/> 实例。
    /// </summary>
    /// <param name="type">选项值的类型。</param>
    /// <returns>对应类型的 <see cref="IOptionBuilder"/> 实例。</returns>
    internal static IOptionBuilder Create(Type type)
    {
        return (IOptionBuilder)Activator.CreateInstance(typeof(OptionBuilder<>).MakeGenericType(type))!;
    }

    /// <summary>
    /// 泛型选项构建器。
    /// </summary>
    /// <typeparam name="T">选项值的类型。</typeparam>
    class OptionBuilder<T> : IOptionBuilder
    {
        /// <inheritdoc/>
        public Option Build(OptionAttribute attribute)
        {
            var option = new Option<T>($"--{attribute.Name}", attribute.Alias!.Select(a => $"-{a}").ToArray())
            {
                Description = attribute.Description,
                Required = attribute.Required!.Value,
            };
            if (attribute.DefaultValueFactory is not null)
            {
                option.DefaultValueFactory = _ => (T?)attribute.DefaultValueFactory();
            }

            if (attribute.ValueType == typeof(object))
            {
                option.Arity = ArgumentArity.Zero;
            }
            else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(attribute.ValueType) && attribute.ValueType != typeof(string))
            {
                var min = option.Required && attribute.DefaultValueFactory is null ? 1 : 0;
                option.Arity = new ArgumentArity(min, ArgumentArity.ZeroOrMore.MaximumNumberOfValues);
            }
            else if (attribute.ValueType!.IsEnum && attribute.ValueType.IsDefined(typeof(FlagsAttribute), false))
            {
                var min = attribute.DefaultValueFactory is not null ? 0 : 1;
                option.Arity = new ArgumentArity(min, ArgumentArity.ZeroOrMore.MaximumNumberOfValues);
            }
            else
            {
                var min = attribute.DefaultValueFactory is not null ? 0 : 1;
                option.Arity = new ArgumentArity(min, 1);
            }

            return option;
        }

        /// <inheritdoc/>
        public object GetValue(Option option, ParseResult result)
        {
            return result.GetValue((Option<T>)option)!;
        }
    }
}